﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CitizenDiary.Sync.Contract;
using Newtonsoft.Json;
using CitizenDiary.Sync.Shared;
using System.Security.Cryptography;
using System.Security;
using System.Xml.Serialization;
using System.IO;
using System.Xml;

namespace CitizenDiary.Sync.Service
{
    internal class SyncTokenProvider
    {
        private ICache _cache;
        private MD5 _md5;

        public SyncTokenProvider(ICache cache)
        {
            _cache = cache;
            _md5 = MD5.Create();
        }

        public void GetToken(SynchronizationCollection items, out string token, out string syncKey)
        {
            var tokenObject = new SyncToken(items);
            var tokenJson = JsonConvert.SerializeObject(tokenObject, Newtonsoft.Json.Formatting.Indented);
            var tokenString = GetToken(tokenJson);

            _cache.Set(tokenObject.SynchronizationKey.ToString(), tokenJson);
            token = tokenString;
            syncKey = tokenObject.SynchronizationKey;
        }

        private string GetToken(string tokenJson)
        {
            var tokenBytes = Encoding.Unicode.GetBytes(tokenJson);
            var tokenMd5 = _md5.ComputeHash(tokenBytes);
            var tokenString = Convert.ToBase64String(tokenMd5);

            return tokenString;
        }

        public SynchronizationCollection MergeSyncItems(string token, string syncKey, SynchronizationCollection deviceSyncItems)
        {
            var tokenJson = _cache.Get(syncKey);
            var tokenExpected = GetToken(tokenJson);
            if (token == tokenExpected)
            {
                var tokenObject = JsonConvert.DeserializeObject<SyncToken>(tokenJson);
                foreach (var deviceSyncItem in deviceSyncItems)
                {
                    if (tokenObject.SynchronizationItems.ContainsKey(deviceSyncItem.Key))
                    {
                        tokenObject.SynchronizationItems[deviceSyncItem.Key] = deviceSyncItem.Value;
                    }
                }
                _cache.Remove(syncKey);
                return tokenObject.SynchronizationItems;
            }
            else
            {
                throw new SecurityException("Invalid token provided.");
            }
        }

        private class SyncToken
        {
            public SynchronizationCollection SynchronizationItems { get; set; }
            public DateTime SynchronizationTime { get; set; }

            public string SynchronizationKey { get; set; }

            public SyncToken()
            {
            }

            public SyncToken(SynchronizationCollection items)
            {
                SynchronizationItems = items;
                SynchronizationTime = DateTime.Now;
                SynchronizationKey = Guid.NewGuid().ToString().ToUpper();
            }
        }
    }
}
